import json
import logging
import jsonpath

from apiTest.models import ApiInfo, ApiSuite
from apiTest.serializers import ApiInfoModelSerializer, ApiSuiteModelSerializer
from collections import OrderedDict

num = 1

logging = logging.getLogger("log")


def analysis_json(file, project_id, url, env_id, base_path, is_file=True):
    """
    接口导入功能
    :param file:
    :param project_id:
    :param url:
    :param env_id:
    :param base_path: api3.0没有项目路径，手动新增项目路径
    :return:
    """
    if is_file:
        string = file_read(file)
    else:
        string = file
    if string:
        json_str = json.loads(string)
        if "swagger" in json_str or "openapi" in json_str:
            # 根据项目创建接口分类
            # set_api_suite(json_str, project_id)
            if "swagger" in json_str:
                get_api_list = get_api_list_by_swagger(json_str, project_id)
            else:
                get_api_list = get_api_list_by_open_api(json_str, project_id, base_path)
            for api_info in get_api_list:
                api_info["domain"] = url
                api_info["env_id"] = env_id
                if ApiInfo.objects.all().filter(path=api_info["path"], project_id=project_id, is_deleted=0).first():
                    api_info_model = ApiInfo.objects.get(path=api_info["path"], project_id=project_id, is_deleted=0)
                    serializer = ApiInfoModelSerializer(api_info_model, data=api_info, partial=True)
                    if serializer.is_valid():
                        serializer.save()
                    else:
                        logging.error(f"api更新失败：{serializer.data},serializer_error:{serializer.errors}")
                else:
                    api_info = ApiInfoModelSerializer(data=api_info)
                    if api_info.is_valid():
                        api_info.save()
                    else:
                        logging.error(f"api保存失败：{api_info.data},serializer_error:{api_info.errors}")

            return "导入成功"
        else:
            return "非swagger格式文件不解析"
    else:
        return "文件不存在"


def get_api_list_by_swagger(json_str: object, project_id: int) -> list:
    """
    解析swagger文档：仅支持json格式
    :param json_str:
    :param project_id:
    :return:
    """
    api_list = []

    paths = json_str.get("paths")
    base_url = json_str.get("basePath")
    for url in paths.keys():
        api_info = {}
        real_url = base_url + url
        api_info["path"] = real_url
        api_info["project_id"] = project_id
        map_dict = paths.get(url)
        for method in map_dict.keys():
            js1 = map_dict.get(method)
            api_info["method"] = method
            if js1.get("summary"):
                api_info["name"] = js1.get("summary")
            else:
                api_info["name"] = url
            tags = js1.get("tags")
            tags = tags[2:-2]
            # 判断是否存在suiteName 塞入ApiSuiteId
            api_suite_list = list(
                ApiSuite.objects.all().filter(name=tags, is_deleted=0, project_id=project_id).values())
            if api_suite_list:
                api_info["api_suite_id"] = api_suite_list[0]["id"]
            else:
                api_suite = {"name": tags, "project_id": project_id}
                api_suite_serializer = ApiSuiteModelSerializer(api_suite)
                if api_suite_serializer.is_valid():
                    api_suite_model = api_suite_serializer.save()
                    api_info["api_suite_id"] = api_suite_model.id
            # 判断json中是否存在parameters
            if "parameters" in js1:
                parameter_list = list(js1.get("parameters"))
                parameter = parameter_list[0]
                if "in" in parameter:
                    request_type = parameter["in"]
                    if request_type == "body":
                        header_list = []
                        header = {"key": "Content-Type", "value": "application/json"}
                        header_list.append(header)
                        api_info["request_header"] = header_list
                        api_info["request_param_type"] = "raw"
                        schema = parameter["schema"]
                        # 判断是否直接存在key：originalRef
                        if "originalRef" in schema:
                            parameters = get_req_body(json_str.get("definitions"), schema.get("originalRef"))
                            api_info["request_body"] = parameters
                        elif "items" in schema:
                            # 不是直接的key: originalRef这外面包了一层key: items
                            items = schema.get("items")
                            if "originalRef" in items:
                                parameters = get_req_body(json_str.get("definitions"), schema.get("originalRef"))
                                api_info["request_body"] = parameters
                            else:
                                parameters = "[]"
                                api_info["request_body"] = parameters
                        elif "additionalProperties" in schema:
                            parameters = "{\n" + "\"additionalProp1\":\"\"\n" + "}"
                            api_info["request_body"] = parameters
                    else:
                        # 塞入入参格式form
                        api_info["request_param_type"] = "form"
                        params_list = []
                        for parameters in parameter_list:
                            parameter = {"key": parameters["name"], "value": ""}
                            params_list.append(parameter)
                        if str(method).lower() == "get":
                            api_info["request_param"] = params_list
                        else:
                            header_list = []
                            header = {"key": "Content-Type", "value": "application/json"}
                            header_list.append(header)
                            api_info["request_header"] = header_list
                            api_info["request_data_params"] = params_list
                else:
                    params_list = []
                    for parameters in parameter_list:
                        parameter = {"key": parameters["name"], "value": ""}
                        params_list.append(parameter)
                    api_info["request_param"] = params_list
            else:
                "parameters:null"
        api_list.append(api_info)

    return api_list


def get_api_list_by_open_api(json_str: object, project_id: int, base_path: str) -> list:
    """
    解析openApi3.0文档：仅支持json格式
    :param json_str:
    :param project_id:
    :param base_path:
    :return:
    """
    api_list = []
    paths = json_str.get("paths")
    for url in paths.keys():
        api_info = {}
        if base_path:
            real_url = base_path + url
            api_info["path"] = real_url
        else:
            api_info["path"] = url
        api_info["project_id"] = project_id
        map_dict = paths.get(url)
        for method in map_dict.keys():
            js1 = map_dict.get(method)
            api_info["method"] = method
            if js1.get("summary"):
                api_info["name"] = js1.get("summary")
            else:
                api_info["name"] = url
            tags = js1.get("tags")
            tags = str(tags)[2:-2]
            # 判断是否存在suiteName 塞入ApiSuiteId
            api_suite_list = list(
                ApiSuite.objects.all().filter(name=tags, is_deleted=0, project_id=project_id).values())
            if api_suite_list:
                api_info["api_suite_id"] = api_suite_list[0]["id"]
            else:
                api_suite = {"name": tags, "project_id": project_id}
                api_suite_serializer = ApiSuiteModelSerializer(data=api_suite)
                if api_suite_serializer.is_valid():
                    api_suite_model = api_suite_serializer.save()
                    api_info["api_suite_id"] = api_suite_model.id
                else:
                    logging.error(f"api分类保存失败：{api_suite_serializer.data}")
            # 判断json中是否存在parameters
            header_list = []
            if "parameters" in js1:
                parameter_list = list(js1.get("parameters"))
                paramters_list = []

                for parameter in parameter_list:
                    if "schema" in parameter and "query" == parameter.get("in"):
                        schema = parameter.get("schema")
                        if "$ref" in schema.keys():
                            ref = schema.get("$ref")
                            properties = json_str.get("components").get("schemas").get(get_ref_data(ref)).get(
                                "properties")
                            for key in properties:
                                parameters = {"key": key, "value": ""}
                                paramters_list.append(parameters)
                        else:
                            parameters = {"key": parameter.get("name"), "value": ""}
                            paramters_list.append(parameters)
                    else:
                        parameters = {"key": parameter.get("name"), "value": ""}
                        paramters_list.append(parameters)
                    if "schema" in parameter and "header" == parameter.get("in"):
                        header = {"key": parameter.get("name"), "value": ""}
                        header_list.append(header)
                        api_info["request_header"] = header_list
                api_info["request_param_type"] = "form"
                if method == "post":
                    api_info["request_data_params"] = paramters_list
                else:
                    api_info["request_param"] = paramters_list
            elif "requestBody" in js1:

                header = {"key": "Content-Type", "value": "application/json"}
                header_list.append(header)
                api_info["request_header"] = header_list
                api_info["request_param_type"] = "raw"
                # openApi3.0 外面又套了一层
                if "$ref" in js1.get("requestBody").get("content").get("application/json").get("schema"):
                    # print(paths,js1.get("requestBody").get("content").get("application/json").get("schema"))
                    item = jsonpath.jsonpath(js1,
                                             '$.requestBody.content.application/json.schema.$ref')[
                        0]
                    item = item.split("/")
                    parameters = get_req_body(json_str.get("components").get("schemas"), item[-1])
                    # print(parameters)
                    # print(f"len{len(item)},apiName:{api_info['name']},request_body:{parameters},schema：{item}")
                    api_info["request_body"] = parameters
                elif js1.get("requestBody").get("content").get("application/json").get("schema").get(
                        "type") == "array" and "$ref" in js1.get("requestBody").get("content").get(
                    "application/json").get("schema").get("items"):
                    item = jsonpath.jsonpath(js1,
                                             '$.requestBody.content.application/json.schema.items.$ref')[
                        0]
                    item = item.split("/")
                    parameters = get_req_body(json_str.get("components").get("schemas"), item[-1])
                    parameters_list = []
                    parameters_list.append(json.loads(parameters))
                    api_info["request_body"] = json.dumps(parameters_list)
                else:
                    api_info["request_body"] = "[]"
        api_list.append(api_info)

    return api_list


def get_req_body(json_object, str_key, max_depth=3):
    """
    获取swagger 中 body数据，可能存在嵌套，采用递归的方式获取
    :param json_object:
    :param str_key:
    :param max_depth:
    :return:
    """
    global num  # 声明num为全局变量
    if num >= max_depth:
        return ""  # 达到最大递归深度，返回空字符串或抛出异常

    # 获取嵌套的JSON对象
    nested_json_object = json_object.get(str_key, {})

    # 获取properties并转换为字典
    properties = nested_json_object.get('properties', {})
    properties_dict = json.loads(json.dumps(properties), object_pairs_hook=OrderedDict)
    # print(properties)

    # 创建一个新的字典来存储结果
    result_dict = OrderedDict()

    for key, value in properties_dict.items():
        if isinstance(value, dict) and '$ref' in value:
            ref_str = value.get('$ref')
            ref_key = get_ref_data(ref_str)
            num += 1  # 增加递归深度计数
            try:
                result_dict[key] = get_req_body(json_object, ref_key, max_depth)
            finally:
                num -= 1  # 递归返回后减少递归深度计数
        elif isinstance(value, dict) and value.get('type') == 'array':
            items = value.get('items', {})
            if isinstance(items, dict) and '$ref' in items:
                ref_str = items.get('$ref')
                ref_key = get_ref_data(ref_str)
                if str_key + '/' + ref_key in json_object:
                    num += 1  # 增加递归深度计数
                    try:
                        array_item = get_req_body(json_object, ref_key, max_depth)
                        result_dict[key] = [json.loads(array_item)]  # 假设array_item是JSON字符串
                    finally:
                        num -= 1  # 递归返回后减少递归深度计数
            else:
                result_dict[key] = []  # 如果不是引用，则初始化为空列表
        else:
            result_dict[key] = ""  # 如果不是数组也不是引用，则赋空字符串

    return json.dumps(result_dict, ensure_ascii=False)


def get_ref_data(ref):
    ref_list = ref.split("/")
    return ref_list[-1]


def file_read(file):
    with open(file, 'r', encoding='utf-8') as f:
        string = f.read()
    return string
